﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using InTheHand.Net.Bluetooth;
using PowerAwareBluetooth.Controller.AI;
using PowerAwareBluetooth.Controller.IO;
using PowerAwareBluetooth.Model;
using PowerAwareBluetooth.Model.Process;

namespace PowerAwareBluetooth.Controller.Manager
{
    /// <summary>
    /// handles the logic, basically an inifinte loop
    /// that samples and change the bluetooth state
    /// </summary>
    public class BluetoothPowerManager : IManager
    {
        #region /// Private Members ///

        private BluetoothAdapter m_BluetoothAdapter;
        private RuleList m_RuleList;
        private DecisionMaker m_DecisionMaker;
        private bool m_NeedUpdateDecisionMakerRuleList = false;
        private bool m_NeedCloseTheManger = false;
        private RadioMode m_lastMode;
        private bool m_NeedResetLearning = false;
        private bool m_userDefinedSamplesPerInterval = false;
        private long m_samplesPerInterval = 4;

        #endregion Members

        /// <summary>
        /// the manager's process name (the name of the executable)
        /// </summary>
        public static readonly string ManagerProcessName =
            Path.ChangeExtension(typeof(BluetoothPowerManager).Assembly.GetName().Name, "exe");

        /// <summary>
        /// gets a flag that indicates if the manager is already running
        /// </summary>
        public static bool? ManagerIsRunning(bool showMessageBox, bool myProcess)
        {
            try
            {
                return ProcessAPI.IsProcessRunning(ManagerProcessName, myProcess);
            }
            catch (Exception e)
            {
                if (showMessageBox)
                {
                    string error = "Unable to check if the manager is running:\n" + e.Message;
                    MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation,
                                    MessageBoxDefaultButton.Button1);
                }
                return null;
            }

        }

        /// <summary>
        /// gets a list of user-defined rules
        /// </summary>
        public RuleList RulesList
        {
            get { return m_RuleList; }
        }

        /// <summary>
        /// creates the bluetooth power manager
        /// 1. initializes the adapter
        /// 2. initializes the decision maker
        /// </summary>
        public BluetoothPowerManager()
        {
            m_BluetoothAdapter = new BluetoothAdapter(this);
            // load rule list from the sim-card/hard disk
            m_RuleList = LoadRuleList();
            m_DecisionMaker = new DecisionMaker(m_BluetoothAdapter, m_RuleList);
            InitWaitMessageThread();
            m_lastMode = m_BluetoothAdapter.RadioMode;
        }

        private RuleList LoadRuleList()
        {
            RuleList retList = IOManager.Load(IOManager.SaveType.USER_DEFINED_RULES) as RuleList;
            if (retList == null)
            {
                retList = new RuleList();
            }
            return retList;
        }

        /// <summary>
        /// manages the blue-tooth according to the <see cref="TimeLine"/>
        /// </summary>
        public void Start()
        {
            long timeToSleepMillisec;
            while (true)
            {
                lock (this)
                {
                    if (m_NeedCloseTheManger)
                    {
                        break;
                    }
                    if (m_NeedResetLearning)
                    {
                        m_DecisionMaker.ResetLearning();
                        m_NeedResetLearning = false;
                    }
                    if (m_NeedUpdateDecisionMakerRuleList)
                    {
                        m_DecisionMaker.RulesList = m_RuleList;
                        m_NeedUpdateDecisionMakerRuleList = false;
                    }
                    // locking our self ensure us that the waking-thread cannot do anything
                    // until we finished resetting the event

                    m_DecisionMaker.ResetInterruptEvent();
                }

                if (m_lastMode != m_BluetoothAdapter.RadioMode)
                {
                    //user changed the mode while we were sleeping
                    m_DecisionMaker.LearnUserChangedMode(m_BluetoothAdapter.RadioMode);
                }
                else
                {
                    //user didn't change the mode while we were sleeping

                    // gets the decision from the DecisionMaker and handles the bluetooth device
                    m_BluetoothAdapter.RadioMode = m_DecisionMaker.RadioModeDecided();
                }

                //sample
                if (!m_userDefinedSamplesPerInterval)
                {
                    m_samplesPerInterval = m_DecisionMaker.RecommendedSamplesPerInterval();
                }

                if (m_DecisionMaker.SampleNeeded())
                {
                    m_DecisionMaker.Sample(m_samplesPerInterval);
                }

                m_lastMode = m_BluetoothAdapter.RadioMode;

                // get the next time to wake-up
                timeToSleepMillisec = m_DecisionMaker.CalculateCurrentWaitTime(m_samplesPerInterval);

                // sleep - no need to lock when sleeping
                m_DecisionMaker.Sleep(timeToSleepMillisec);
            }
        }

        #region Threads

        /// <summary>
        /// initializes a thread that waits for messages from another process
        /// the messages are changes to the user-defined rules.
        /// </summary>
        private void InitWaitMessageThread()
        {
            Thread thread = new Thread(WaitMessageThreadMethod);
            thread.Name = "WaitForMessageThread";
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// a infinite loop that checks the messages-queue for new messages
        /// and blocks until a new message arrives to the queue
        /// </summary>
        private void WaitMessageThreadMethod()
        {
            WinMessageAdapter.Init();
            while(true)
            {
                // waits for a new message
                WinMessageAdapter.NotificationEnum notification = WinMessageAdapter.WaitForMessage();

                switch(notification)
                {
                    case WinMessageAdapter.NotificationEnum.LIST_CHANGED:
                        RuleList newRuleList = IO.IOManager.Load(IOManager.SaveType.USER_DEFINED_RULES) as RuleList;
                        if (newRuleList != null)
                        {
                            lock (this)
                            {
                                m_RuleList = newRuleList;
                                m_NeedUpdateDecisionMakerRuleList = true;

                                m_DecisionMaker.WakeUp();

                            }
                        }
                        break;
                    case WinMessageAdapter.NotificationEnum.CLOSE_MANAGER:
                        lock (this)
                        {
                            m_NeedCloseTheManger = true;
                            m_DecisionMaker.WakeUp();

                            // the thread now exits;
                            return;
                        }
                    case WinMessageAdapter.NotificationEnum.RESET_LEARNING:
                        lock (this)
                        {
                            m_NeedResetLearning = true;
                            m_DecisionMaker.WakeUp();
                        }
                        break;
                }
            }
        }

        #endregion Threads

        #region IManager Members

        public bool Closing
        {
            get
            {
                lock (this)
                {
                    return m_NeedCloseTheManger;
                }
            }
        }

        #endregion
    }
}
